#include <iostream>
#include <functional>
#include <memory>
#include <vector>
#include <unordered_map>
#include <unistd.h>
#include <ctime>

using TaskFunc = std::function<void()>;

class TimerTask
{
    using ReleaseFunc = std::function<void()>;
public:    
    TimerTask(uint64_t id, uint32_t timeout, const TaskFunc &cb)        // 构造
        :_id(id), _timeout(timeout), _task_cb(cb), _canceled(false)
    {}

    ~TimerTask()        // 析构，需要间接回调任务，同时释放掉 TimerWheel 中哈希表里存储的任务对象信息
    {
        if(_canceled == false)
            _task_cb();
        _release();
    }

    void Cancel()       // 设置本定时任务对象 是否被取消
    {
        _canceled = true;
    }

    void SetRelease(const ReleaseFunc &cb)      // 设置回调
    {
        _release = cb;
    }

    uint32_t GetDelayTime()
    {
        return _timeout;
    }

private:
    uint64_t _id;		    // 定时器对象的任务id，应由外部统一为多线程分配
    uint32_t _timeout;	    // 定时任务的超时时间
    TaskFunc _task_cb;		// 需要被回调的任务，由外部传入 —— 本组件中，应当是 非活跃连接的销毁 操作
    ReleaseFunc _release;	// 释放哈希表中任务对象信息的回调
    bool _canceled;         // 该定时任务是否被取消
};


class TimerWheel
{
    using PtrTask = std::shared_ptr<TimerTask>;
    using WeakTask = std::weak_ptr<TimerTask>;

public:
    TimerWheel()        // 构造，对秒针、表盘长度进行初始化
        :_tick(0), _capacity(60), _wheel(_capacity)
    {}

    void TimerAdd(uint64_t id, uint32_t delay, const TaskFunc &cb)      // 往时间轮中添加定时任务，及其任务参数
    {
        PtrTask pt(new TimerTask(id, delay, cb));       // 实例化一个任务对象
        pt->SetRelease(std::bind(&TimerWheel::RemoveTimer, this, id));       // 设置任务对象被销毁时的回调，并绑定 this
        int pos = (_tick + delay) % _capacity;
        _wheel[pos].push_back(pt);      // 把任务对象添加到 二维时间轮中
        _timers[id] = WeakTask(pt);     // 添加到 哈希表中保存
    }

    void TimerRefresh(uint64_t id)      // 刷新或延迟定时任务，涉及到需要拷贝 原管理此对象智能指针，那么需要引入weak_ptr 对 shared_ptr 进行存储，这样并不会增加 shared_ptr 的引用计数；并引入 unordered_map 记录任务id 与 对应 weak ptr，以便需要延迟任务时，找到并进行拷贝构造
    {
        auto it = _timers.find(id);     // 从哈希表中找到保存好的 weak ptr，去构造新的 shared ptr，再根据超时时间，添加到时间轮中
        if(it != _timers.end())
        {
            // PtrTask new_pt(_timers[id]);
            PtrTask new_pt = it->second.lock();     // lock 会返回 weak ptr 管理的对象的 shared ptr
            int pos = (_tick + new_pt->GetDelayTime()) % _capacity;     // 任务被刷新后 该存放的位置
            _wheel[pos].push_back(new_pt);      // 刷新的定时任务，放入时间轮中新的位置
        }
    }

    void TimerCancel(uint64_t id)       // TimerTask 对象只有一个，改哈希表里的和 时间轮中的，一样
    {
        auto it = _timers.find(id);     
        if(it != _timers.end())
        {
            PtrTask new_pt = it->second.lock();
            new_pt->Cancel();       // 必须还原出 shared ptr 再调用 TimerTask 的方法
        }
    }

    void RunTimerTask()     // 每秒钟执行一次本函数，让 tick 向后走一步，每走一步都执行对应任务
    {
        _tick = (++_tick) % _capacity;
        _wheel[_tick].clear();      // clear 可以销毁该 vector 中的所有元素，这样就达到了销毁 TimerTask 任务对象的 shared ptr 的目的，当引用计数为0时，就会回调 两个函数
    }

private:
    void RemoveTimer(uint64_t id)       // 为 TimerAdd 时设置回调，封装的定时任务析构需要执行的从 哈希表中移除该任务的操作
    {
        auto it = _timers.find(id);
        if(it != _timers.end())
        {
            _timers.erase(it);
        }
    }
    
private:
    int _tick;                                          // 秒针
    int _capacity;                                      // 表盘的长度，即最长的可延迟时间
    std::vector<std::vector<PtrTask>> _wheel;           // 每级时间轮都是二维数组，存储管理任务对象的智能指针
    std::unordered_map<uint64_t, WeakTask> _timers;     // 存储每个任务对象的 shared_ptr，以便刷新任务时进行拷贝；但，当该任务对象真正被析构 以回调任务时，本哈希表中对应 id 的 weak ptr 也应被删除掉；所以这个操作放在 TimerTask 的析构中，合理
};

